package com.ruoyi.framework.config;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import java.util.Set;

@WebFilter(urlPatterns = "/*")
public class SqlInjectionFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String[] urls = {"/ajaxLogin","/logout","/preLogin","/dataSync"};

        String url =request.getRequestURL().toString();
        for (String u:urls) {
            if (url.contains(u)) {
                filterChain.doFilter(request, response);
                return;
            }
        }
        Map<String, String[]> parameterMap = request.getParameterMap();
        if (parameterMap.containsKey("solt")) {
            try {
                String[] value = parameterMap.get("solt");
                for(String i : value) {
                    Long.parseLong(i);
                }
            } catch (Exception e) {
                response.sendError(HttpServletResponse.SC_BAD_REQUEST);
                return;
            }
        }

        String contentType = request.getContentType();
        BodyReaderRequestWrapper wrapper = null;
        if ("POST".equals(request.getMethod()) && contentType.contains("application/json")) {
            wrapper = new BodyReaderRequestWrapper(request);
            String requestPostStr = wrapper.getBody();
            if (requestPostStr.startsWith("{")) {
                //解析json对象
                boolean b = resolveJSONObjectObj(requestPostStr);
                if (!b) {
                    response.sendError(HttpServletResponse.SC_BAD_REQUEST);
                    return;
                }
            } else if (requestPostStr.startsWith("[")) {
                //把数据转换成json数组
                JSONArray jsonArray = JSONArray.parseArray(requestPostStr);
                if(!jsonArray.isEmpty()) {
                    for (Object o : jsonArray) {
                        //解析json对象
                        if(o != null) {
                            boolean b = resolveJSONObjectObj(o.toString());
                            if (!b) {
                                response.sendError(HttpServletResponse.SC_BAD_REQUEST);
                                return;
                            }
                        }
                    }
                }
            }
        } else {
            //application/x-www-form-urlencoded
            for (Map.Entry<String,String[]> entry : parameterMap.entrySet()) {
                //校验参数值是否合法
                String[] value = entry.getValue();
                for (String s : value) {
                    //校验参数值是否合法
                    boolean b = verifySql(s);
                    if (!b) {
                        response.sendError(HttpServletResponse.SC_BAD_REQUEST);
                        return;
                    }
                }
            }
        }
        if (wrapper == null) {
            filterChain.doFilter(servletRequest,servletResponse);
        } else {
            filterChain.doFilter(wrapper,servletResponse);
        }
    }

    /**
     * 对JSONObject对象进行递归参数解析
     * @param requestPostStr
     * @return
     */
    private boolean resolveJSONObjectObj(String requestPostStr) {
        boolean isover = true;
        try {
            // 创建需要处理的json对象
            JSONObject jsonObject = JSONObject.parseObject(requestPostStr);
            // 获取所有的参数key
            Set<String> keys = jsonObject.keySet();
            if (keys.size() > 0) {
                for (String key : keys) {
                    //获取参数名称
                    String value;
                    if (jsonObject.get(key) != null) {
                        value = String.valueOf(jsonObject.get(key));
                        //当value为数组时
                        if (value.startsWith("[")) {
                            //把数据转换成json数组
                            JSONArray jsonArray = JSONArray.parseArray(value);
                            for (Object o : jsonArray) {
                                //解析json对象
                                if (o != null) {
                                    boolean b = resolveJSONObjectObj(o.toString());
                                    if (!b) {
                                        isover = false;
                                        break;
                                    }
                                }
                            }
                        } else if (value.startsWith("{")) {
                            boolean b = resolveJSONObjectObj(value);
                            if (!b) {
                                isover = false;
                                break;
                            }
                        } else {
                            //校验参数值是否合法
                            boolean b = verifySql(value);
                            if (!b) {
                                isover = false;
                                break;
                            }
                        }
                    }
                }
            }
        }catch(Throwable t){
            boolean b = verifySql(requestPostStr);
            if (!b) {
                isover = false;
            }
        }
        return isover;
    }

    @Override
    public void destroy() {

    }

    /**
     * 校验参数非法字符
     */
    public boolean verifySql(String parameter) {
        if (StringUtils.isNotBlank(parameter) && (
                parameter.toLowerCase().contains("select")
                        || parameter.toLowerCase().contains("update")
                        || parameter.toLowerCase().contains("delete")
                        || parameter.toLowerCase().contains("insert")
                        || parameter.toLowerCase().contains("truncate")
                        || parameter.toLowerCase().contains("substr")
                        || parameter.toLowerCase().contains("char")
                        || parameter.toLowerCase().contains("exec")
                        || parameter.toLowerCase().contains("master")
                        || parameter.toLowerCase().contains("drop")
                        || parameter.toLowerCase().contains("execute")
                        || parameter.toLowerCase().contains("xp_cmdshell")
                        || parameter.toLowerCase().contains("use")
                        || parameter.toLowerCase().contains("column_name")
                        || parameter.toLowerCase().contains("information_schema.columns")
                        || parameter.toLowerCase().contains("information_schema.system_columns")
                        || parameter.toLowerCase().contains("table_schema")
                        || parameter.toLowerCase().contains("union")
                        || parameter.toLowerCase().contains("sleep")
                        || parameter.toLowerCase().contains("thread")
                        || parameter.toLowerCase().contains("case when")
                        || parameter.toLowerCase().contains("case")
                        || parameter.toLowerCase().contains("randomblob")
                        || parameter.toLowerCase().contains("random")
                        || parameter.toLowerCase().contains("set")
                        || parameter.toLowerCase().contains("cookie")
                        || parameter.toLowerCase().contains("save")
                        || parameter.toLowerCase().contains(".properties")
                        || parameter.toLowerCase().contains(".sh")
                        || parameter.toLowerCase().contains(".dll")
                        || parameter.toLowerCase().contains(".exe")
                        || parameter.toLowerCase().contains(".yml")
                        || parameter.toLowerCase().contains(".xml")
                        || parameter.toLowerCase().contains(".js")
                        || parameter.toLowerCase().contains("../")
                        || parameter.toLowerCase().contains("./")
                        || parameter.toLowerCase().contains("..")
                        || parameter.toLowerCase().contains("--")
                        || parameter.toLowerCase().contains("#")
                        || parameter.toLowerCase().contains("/*")
                        || parameter.toLowerCase().contains("*/")
                        || parameter.toLowerCase().contains("'")
                        || parameter.toLowerCase().contains("eval")
                        || parameter.toLowerCase().contains("script"))
        ) {
            return false;
        } else {
            return true;
        }
    }
}
